﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using Tyche.Common.Util.Utils;

namespace Tyche.Common.Util.Models.Entity
{
    public class WeiXinPayNotifyEntity
    {
        /// <summary>
        /// AppId
        /// </summary>
        public string AppId { get; set; }

        /// <summary>
        /// 商户号
        /// </summary>
        public string MchId { get; set; }

        /// <summary>
        /// 设备号
        /// </summary>
        public string DeviceInfo { get; set; }

        /// <summary>
        /// 随机字符串
        /// </summary>
        public string NonceStr { get; set; }

        /// <summary>
        /// OpenId
        /// </summary>
        public string OpenId { get; set; }

        /// <summary>
        /// 是否关注公众账号
        /// </summary>
        public string IsSubscribe { get; set; }

        /// <summary>
        /// 交易类型
        /// </summary>
        public string TradeType { get; set; }

        /// <summary>
        /// 付款银行
        /// </summary>
        public string BankType { get; set; }

        /// <summary>
        /// 订单金额
        /// </summary>
        public int TotalFee { get; set; }

        /// <summary>
        /// 应结订单金额
        /// </summary>
        public int SettlementTotalFee { get; set; }

        /// <summary>
        /// 货币种类
        /// </summary>
        public string FeeType { get; set; }

        /// <summary>
        /// 现金支付金额
        /// </summary>
        public int CashFee { get; set; }

        /// <summary>
        /// 现金支付货币类型
        /// </summary>
        public string CashFeeType { get; set; }

        /// <summary>
        /// 总代金券金额
        /// </summary>
        public int CouponFee { get; set; }

        /// <summary>
        /// 代金券使用数量
        /// </summary>
        public int CouponCount { get; set; }

        /// <summary>
        /// 代金券类型	
        /// </summary>
        public IEnumerable<string> CouponTypes { get; set; }

        /// <summary>
        /// 代金券ID	
        /// </summary>
        public IEnumerable<string> CouponIds { get; set; }

        /// <summary>
        /// 代金券支付金额	
        /// </summary>
        public IEnumerable<int> CouponFees { get; set; }

        /// <summary>
        /// 微信支付订单号
        /// </summary>
        public string TransactionId { get; set; }

        /// <summary>
        /// 商户订单号
        /// </summary>
        public string OutTradeNo { get; set; }

        /// <summary>
        /// 商家数据包
        /// </summary>
        public string Attach { get; set; }

        /// <summary>
        /// 支付完成时间
        /// </summary>
        public string TimeEnd { get; set; }

        /// <summary>
        /// Xml转换
        /// </summary>
        /// <param name="xml"></param>
        /// <returns></returns>
        public static WeiXinPayNotifyEntity Parse(string xml, string key)
        {
            var document = XDocument.Parse(xml);

            if (document == null || document.Element("xml") == null)
            {
                throw new Exception("微信支付回调处理失败: 报文格式错误");
            }

            var parameters = new SortedDictionary<string, string>(document.Element("xml")
                .Elements()
                .ToDictionary(item => item.Name.ToString(), item => item.Value));

            if (parameters["return_code"] != "SUCCESS")
            {
                throw new Exception(string.Format("微信支付回调处理失败: {0}", parameters["return_msg"]));
            }

            if (parameters["result_code"] != "SUCCESS")
            {
                throw new Exception(string.Format("微信支付回调处理失败: err_code = {0}, err_code_des = {1}", parameters["err_code"], parameters["err_code_des"]));
            }

            parameters.Remove("sign");

            if (WeiXinUtil.GeneratePaySignature(parameters, key) != document.Element("xml").Element("sign").Value)
            {
                throw new Exception("微信支付回调处理失败: 验签失败");
            }

            var entity = new WeiXinPayNotifyEntity();

            entity.AppId = parameters.ContainsKey("appid") ? parameters["appid"].ToString() : null;
            entity.MchId = parameters.ContainsKey("mch_id") ? parameters["mch_id"].ToString() : null;
            entity.DeviceInfo = parameters.ContainsKey("device_info") ? parameters["device_info"].ToString() : null;
            entity.NonceStr = parameters.ContainsKey("nonce_str") ? parameters["nonce_str"].ToString() : null;
            entity.OpenId = parameters.ContainsKey("openid") ? parameters["openid"].ToString() : null;
            entity.IsSubscribe = parameters.ContainsKey("is_subscribe") ? parameters["is_subscribe"].ToString() : null;
            entity.TradeType = parameters.ContainsKey("trade_type") ? parameters["trade_type"].ToString() : null;
            entity.BankType = parameters.ContainsKey("bank_type") ? parameters["bank_type"].ToString() : null;
            entity.TotalFee = parameters.ContainsKey("total_fee") ? Convert.ToInt32(parameters["total_fee"]) : 0;
            entity.SettlementTotalFee = parameters.ContainsKey("settlement_total_fee") ? Convert.ToInt32(parameters["settlement_total_fee"]) : 0;
            entity.FeeType = parameters.ContainsKey("fee_type") ? parameters["fee_type"].ToString() : null;
            entity.CashFee = parameters.ContainsKey("cash_fee") ? Convert.ToInt32(parameters["cash_fee"]) : 0;
            entity.CashFeeType = parameters.ContainsKey("cash_fee_type") ? parameters["cash_fee_type"].ToString() : null;
            entity.CouponFee = parameters.ContainsKey("coupon_fee") ? Convert.ToInt32(parameters["coupon_fee"]) : 0;
            entity.CouponCount = parameters.ContainsKey("coupon_count") ? Convert.ToInt32(parameters["coupon_count"]) : 0;
            entity.CouponTypes = parameters.Where(p => p.Key.StartsWith("coupon_type_")).Select(p => p.Value);
            entity.CouponIds = parameters.Where(p => p.Key.StartsWith("coupon_id_")).Select(p => p.Value);
            entity.CouponFees = parameters.Where(p => p.Key.StartsWith("coupon_fee_")).Select(p => Convert.ToInt32(p.Value));
            entity.TransactionId = parameters.ContainsKey("transaction_id") ? parameters["transaction_id"].ToString() : null;
            entity.OutTradeNo = parameters.ContainsKey("out_trade_no") ? parameters["out_trade_no"].ToString() : null;
            entity.Attach = parameters.ContainsKey("attach") ? parameters["attach"].ToString() : null;
            entity.TimeEnd = parameters.ContainsKey("time_end") ? parameters["time_end"].ToString() : null;

            return entity;
        }
    }
}
